package mediaCoreLib.managers
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;
	
	import mediaCoreLib.controls.IMediaControl;
	import mediaCoreLib.core.PlaylistObject;
	import flash.media.SoundMixer;

	public class PlaylistManager extends EventDispatcher implements IPlaylistManager
	{
		static private var _prevLoadedMedia:Dictionary = new Dictionary();
		
		//////////////////////////////////////////////////////////
		//	IS PLAYING
		//////////////////////////////////////////////////////////
		
		protected var playing:Boolean = false;
		
		[Bindable("isPlayingChanged")]
		public function get isPlaying ():Boolean
		{
			return playing;
		}
		
		public function set isPlaying (value:Boolean):void
		{
			if (playing != value)
			{
				playing = value;
				dispatchEvent(new Event("isPlayingChanged"));
			}
		}
		
		//////////////////////////////////////////////////////////
		//	INDEX
		//////////////////////////////////////////////////////////
		
		private var _index:int = -1;
		
		[Bindable("selectedIndexChanged")]
		public function get selectedIndex ():int
		{
			return _index;
		}
		
		public function set selectedIndex (value:int):void
		{
			if (_index != value)
			{
				_index = value;
				
				SoundMixer.stopAll();
				
				currentPosition = 0.0;
				
				if (currentMediaControl)
					currentMediaControl.isPlaying = false;
				
				dispatchEvent(new Event("selectedIndexChanged"));
			}
		}
		
		//////////////////////////////////////////////////////////
		//	PLAYLIST
		//////////////////////////////////////////////////////////
		
		private var _playlist:Array = [];
		
		[Bindable("playlistChanged")]
		public function get playlist ():Array
		{
			return _playlist;
		}
		
		public function set playlist (value:Array):void
		{
			if (_playlist.toString() != value.toString())
			{
				_playlist = value;
				selectedIndex = 0;
				
				downloadPlaylistMedia(_playlist);
				
				dispatchEvent(new Event("playlistChanged"));
			}
		}
		
		//////////////////////////////////////////////////////////
		//	IPLAYLIST
		//////////////////////////////////////////////////////////
		
		public function play (mediaControl:IMediaControl = null):void
		{
			var i:int = Math.min(selectedIndex, _soundQueue.length - 1);
			currentPlaylistObj = PlaylistObject(_soundQueue[i]);
			
			if (currentPlaylistObj)
			{
				if (currentPlaylistObj.isLoaded)
					startPlaylist(mediaControl);
				
				else
				{	
					currentMediaControl = mediaControl;
					currentPlaylistObj.addEventListener("isLoadedChanged", onIsLoadedChanged_playHandler);
				}
			}
		}
		
		private function startPlaylist (m:IMediaControl = null):void
		{
			if (!currentPlaylistObj)
				currentPlaylistObj = PlaylistObject(_soundQueue[selectedIndex]);
			
			currentChannel = currentPlaylistObj.sound.play(currentPosition)
			currentChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete_channelHandler);
			
			isPlaying = true;
			
			if (currentMediaControl && currentMediaControl.isPlaying && currentMediaControl != m)
				currentMediaControl.isPlaying = false;
				
			if (m)
			{
				currentMediaControl = m;
				currentMediaControl.isPlaying = true;
			}	
		}
		
		public function stop ():void
		{
			isPlaying = false;
			
			if (currentChannel)
			{
				currentPosition = currentChannel.position;
				currentChannel.stop();
				currentChannel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete_channelHandler);
			}
			
			if (currentMediaControl)
				currentMediaControl.isPlaying = false;
		}
		
		public function reset ():void
		{
			SoundMixer.stopAll();
			
			selectedIndex = -1;
			isPlaying = currentMediaControl.isPlaying = false;
		}
		
		//////////////////////////////////////////////////////////
		//	MISC
		//////////////////////////////////////////////////////////
		
		private var _isFirstSound:Boolean = false;
		
		private var _soundQueue:Array = [];//storage point for sounds. e.g. {url:String, media:Sound}
		
		protected var currentMediaControl:IMediaControl;
		
		protected var currentChannel:SoundChannel;
		protected var currentPosition:Number = 0;
		
		protected var currentPlaylistObj:PlaylistObject;
		
		private function downloadPlaylistMedia (urls:Array):void
		{			
			_soundQueue = [];
			
			var url:String;
			var plObj:PlaylistObject;
			for each (url in urls)
			{
				if (_prevLoadedMedia[url] == null)
				{
					plObj = new PlaylistObject();
					plObj.url = url;
					plObj.addEventListener("isLoadedChanged", onIsLoadedChanged);
					
					_prevLoadedMedia[url] = plObj;
				}
				
				else
					plObj = _prevLoadedMedia[url];
					
				_soundQueue.push(plObj);
			}
		}
		
		//////////////////////////////////////////////////////////
		//	EVT HANDLERS
		//////////////////////////////////////////////////////////
		
		private function onIsLoadedChanged (evt:Event):void
		{
			var plObj:PlaylistObject = PlaylistObject(evt.target);
			plObj.removeEventListener("isLoadedChanged", onIsLoadedChanged);
		}
		
		private function onIsLoadedChanged_playHandler (evt:Event):void
		{
			onIsLoadedChanged(evt);
			
			startPlaylist();
		}
		
		//handler for successful playing of sound
		private function onSoundComplete_channelHandler (evt:Event):void
		{
			var channel:SoundChannel = SoundChannel(evt.target);
			channel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete_channelHandler);
			
			if (selectedIndex != -1 && selectedIndex < _soundQueue.length - 1)
			{
				selectedIndex++;
				
				currentPlaylistObj = PlaylistObject(_soundQueue[selectedIndex]);
				
				currentChannel = currentPlaylistObj.sound.play();
				currentChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete_channelHandler)
			}
			
			else
			{
				selectedIndex = -1;
				
				isPlaying = false;
				
				if (currentMediaControl)
					currentMediaControl.isPlaying = false;
			}
		}
		
		//////////////////////////////////////////////////////////
		//	GET INSTANCE (singleton)
		//////////////////////////////////////////////////////////
		
		static private var _instance:PlaylistManager;
		
		static public function getInstance ():PlaylistManager
		{
			if (!_instance)
				_instance = new PlaylistManager();
				
			return _instance;
		}
		
		//////////////////////////////////////////////////////////
		//	CONSTRUCTOR (singleton for mxml)
		//////////////////////////////////////////////////////////
		
		public function PlaylistManager ()
		{
			super();
			
			if (_instance)
			{
				throw new Error("PlaylistManager is a Singleton-type class.  Only one instance can be instantiated");
				return;
			}
			
			_instance = this;
		}
	}
}